package com.sc.admin.common.aspect;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sc.admin.common.result.Result;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Component;

import java.io.OutputStream;
import java.io.Serializable;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;

@Component
@Aspect
@Slf4j
public class ControllerAspect {
    /**
     * 打印日志的限制长度
     */
    private static final Integer LIMIT_LENGTH = 5000;
    private static ObjectMapper objMapper;

    @Pointcut("within(com.sc.admin.controller..*) ")
    public void controllerPointcut() {
    }


    @Autowired
    public ControllerAspect(ObjectMapper objMapper) {
        ControllerAspect.objMapper = objMapper;
    }

    @Around("controllerPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Instant startTime = Instant.now();

        Object result;
        try {
            result = joinPoint.proceed(joinPoint.getArgs());
        } catch (Throwable e) {
            this.printErrorLog(joinPoint, Duration.between(startTime, Instant.now()).toMillis(), e);
            return Result.failed(e.getMessage());
        }
        this.printLog(joinPoint, result, Duration.between(startTime, Instant.now()).toMillis());
        return result;
    }

    /**
     * jackson输出
     *
     */
    public static String objMapper(Object obj) {
        if (obj == null) {
            return "null";
        }
        String res = "";
        try {
            res = objMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.info("PRO objMapper.writeValueAsString 异常，error:", e);
        }
        return res;
    }

    private void printErrorLog(ProceedingJoinPoint pjp, long millis, Throwable e) {
        try {
            String classPath = pjp.getSignature().getDeclaringTypeName();
            // 调用的方法名
            String methodName = pjp.getSignature().getName();
            log.error("ERROR 方法={}#{}，执行时间：{}ms，{},错误信息:", classPath, methodName, millis, this.splitParamsByMaxLength(this.getParamsInfo(pjp.getArgs())), e);
        } catch (Exception ex) {
            log.warn("PRO ControllerAspect.printErrorLog error.", ex);
        }
    }

    protected void printLog(ProceedingJoinPoint pjp, Object result, long executeTime) {
        try {
            String classPath = pjp.getSignature().getDeclaringTypeName();
            // 调用的方法名
            String methodName = pjp.getSignature().getName();
            StringBuilder afterSb = new StringBuilder();
            if (Objects.isNull(result)) {
                afterSb.append("出参={}");
            } else {
                afterSb.append("出参=").append(this.splitParamsByMaxLength(result instanceof Serializable ? objMapper(result) : result.toString()));
            }
            log.info("方法={}#{}，执行时间：{}ms，{}，{}", classPath, methodName, executeTime, this.splitParamsByMaxLength(this.getParamsInfo(pjp.getArgs())), afterSb);

        } catch (Exception e) {
            log.warn("PRO ControllerAspect.printLog error.", e);
        }

    }

    /**
     * 截取指定长度
     *
     * @param params 参数日志
     * @return 截取最大长度后的字符串
     */
    protected String splitParamsByMaxLength(String params) {

        if (StringUtils.isBlank(params)) {
            return "";
        }

        if (params.length() > LIMIT_LENGTH) {
            params = params.substring(0, LIMIT_LENGTH);
        }
        return params;
    }

    /**
     * 获取接口调用入参信息
     *
     * @param args 参数集合
     * @return 入参信息
     */
    protected String getParamsInfo(Object[] args) {
        StringBuilder beforeSb = new StringBuilder();
        beforeSb.append("入参=[");
        if (Objects.nonNull(args) && args.length > 0) {
            int size = args.length;
            for (int i = 0; i < size; i++) {
                if (this.ignoreClassType(args[i])) {
                    continue;
                }
                beforeSb.append(objMapper(args[i]));
                if (i + 1 != size) {
                    beforeSb.append(",");
                }

            }
        }
        beforeSb.append("]");
        return beforeSb.toString();
    }

    /**
     * 忽略类 不进行json串转换
     *
     */
    protected boolean ignoreClassType(Object obj) {
        return obj instanceof InputStreamSource || obj instanceof OutputStream || obj instanceof ServletRequest || obj instanceof ServletResponse;
    }

}